package com.paat.generate.core;

import com.paat.generate.utils.Pair;
import com.paat.generate.utils.StringUtils;
import jetbrick.template.JetEngine;
import jetbrick.template.JetTemplate;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Stream;

import static com.paat.generate.constants.Constant.*;

/**
 * paat 生成器
 *
 * @author qi.ren@paat.com
 * @since 2019/4/18
 */
@Slf4j
public class PaatGenerator {

	private final ProjectInfo config;
	private final JetEngine   engine;

	private final Map<String, Object> context;

	private File rootDir;

	public PaatGenerator(ProjectInfo config) {
		this.config = config;
		this.engine = JetEngine.create();
		this.context = new HashMap<>(1);
		this.context.put("project", config);
	}

	public void execute() throws IOException {
		log.info("开始生成");
		// 创建项目目录
		if(StringUtils.isNotEmpty(config.getRootProjectPath())){
			this.rootDir = new File(config.getRootProjectPath());
			if (!this.rootDir.exists()) {
				this.rootDir.mkdir();
				this.writeVm(rootDir, POM_NAME, "root_pom.vm");
				this.writeVm(rootDir, README_NAME, "readme.vm");
				this.writeVm(rootDir, GITIGNORE_NAME, "gitignore.vm");
			}
		} else {
			this.rootDir = new File(config.getRootProjectName());
			if (!this.rootDir.exists()) {
				this.rootDir.mkdir();
			}
			this.writeVm(rootDir, POM_NAME, "root_pom.vm");
			this.writeVm(rootDir, README_NAME, "readme.vm");
			this.writeVm(rootDir, GITIGNORE_NAME, "gitignore.vm");
		}

		log.info("项目根目录: {}", this.rootDir.getPath());



		this.writeVm(rootDir, config.getServiceName() + "-service.yml", "service_yml.vm");

		this.generateApi();
		this.generateService();

		log.info("生成成功！");
	}

	private void generateApi() throws FileNotFoundException {
		String           apiDir = "src/main/java/%s";
		Pair<File, File> pair   = this.mkdir(rootDir, API_NAME, apiDir);

		this.writeVm(pair.getK(), POM_NAME, "api/pom.vm");

		this.mkdirsAndWritePackageInfo(pair.getV(), "constant", "enums", "feign", "fallback", "vo", "param", "utils");

		this.writeVm(pair.getV(), "constant/ConfigConst.java", "api/const.vm");
	}

	private void generateService() throws FileNotFoundException {
		String           serviceDir = "src/main/java/%s";
		Pair<File, File> pair       = this.mkdir(rootDir, SERVICE_NAME, serviceDir);

		String prefix = "service";

		// 创建 resources 和 config 目录
		File resourcesDir = this.mkdir(pair.getK(), RESOURCES_DIR);

		// 创建 2 个空包
		this.mkdirsAndWritePackageInfo(pair.getV(), "controller", "config", "service", "entity", "mapper");

		// 创建项目根目录下的 pom.xml 文件
		this.writeVm(pair.getK(), POM_NAME, prefix + "/pom.vm");

		this.writeVm(resourcesDir, BOOTSTRAP_YML_NAME, prefix + "/bootstrap_config.vm");

		// 创建启动类文件
		String bootName = StringUtils.firstUpper(config.getServiceName()) + "Application.java";
		this.writeVm(pair.getV(), bootName, prefix + "/source/application.vm");

		// 生成测试代码
		String testPackagePath = config.getPackageName().replace(".", "/");
		this.mkdirs(new File(pair.getK(), "src"), "test/java/" + testPackagePath);
		this.writeVm(new File(pair.getK(), "src/test/java/" + testPackagePath), "BaseTest.java", "service/source/basetest.vm");
	}

	private void mkdirs(File parent, String... paths) {
		Stream.of(paths).forEach(path -> this.mkdir(parent, path));
	}

	private void mkdirsAndWritePackageInfo(File parent, String... paths) {
		Stream.of(paths).forEach(path -> {
			File mkdir = this.mkdir(parent, path);
			try {
				this.writeVm(mkdir, "package-info.java", "/packageinfo.vm");
			} catch (FileNotFoundException e) {
				log.error("", e);
			}
		});
	}

	private File mkdir(File parent, String path) {
		File dir = new File(parent, path);
		dir.mkdirs();
		return dir;
	}

	private void writeVm(File parent, String fileName, String templateName) throws FileNotFoundException {
		File file = new File(parent, fileName);
		if (config.isOverride() && file.exists()) {
			file.delete();
		}
		JetTemplate template = engine.getTemplate("templates/" + templateName);
		context.put("dirName", parent.getName());
		template.render(context, new FileOutputStream(file));
	}

	/**
	 * @return 返回项目路径和基础包路径
	 */
	private Pair<File, File> mkdir(File parentDir, String dirName, String basePackage) {
		File dir = mkdir(parentDir, String.format(dirName, config.getServiceName()));

		String packagePath = String.format(basePackage, config.getPackageName().replace(".", "/"));

		File packageDir = mkdir(dir, packagePath);

		return new Pair<>(dir, packageDir);
	}

}
